<?php
namespace app\common;
use think\facade\Cache;
use think\facade\Config;
use think\Exception;
use app\admin\model\Province;
use app\admin\model\City;
use app\admin\model\Area;
use app\admin\model\AdminGroup;
class CacheTool
{
	/**
	* requestRoleSet 登录次数IP验证规则模拟缓存
	* @param  string $name   请求标识
	* @param  int $num  限制请求次数 默认30次
	* @param  int $time 设置从新开启时常 默认1小时
	* @param  string $tag  设置缓存标签 默认空
	* @return string	条件不匹配时候返回提示性字符串
	* @return bool	 符合条件是返回真
	*/
	public static function requestRoleSet($name, $num, $time=3600, $tag='')
	{

		/**
		 * requestRoleSet_check 检测传入数据是否合法
		 * @param  string $name   请求标识
		 * @param  int $num  限制请求次数 默认30次
		 * @param  int $time 设置从新开启时常 默认1小时
		 * @param  string $tag  设置缓存标签 默认空
		 */
		$requestRoleSet_check = function($name,$num,$time,$tag)
		{
			// 检测传参数据是否为空
			if (empty($tag)) $error = 'Cachetool_requestRoleSet_Paramone_Notempty';
			if (empty($name)) $error = 'Cachetool_requestRoleSet_Paramtwo_Notempty';
			// 检测传参类型是否合法
			if (!is_string($tag)) $error = 'Cachetool_requestRoleSet_Paramone_Needstring';
			if (!is_string($name)) $error = 'Cachetool_requestRoleSet_Paramone_Needint';
			if (!is_int($time)) $error = 'Cachetool_requestRoleSet_Paramthree_Needint';
			if (!is_string($tag)) $error = 'Cachetool_requestRoleSet_Paramfour_Needint';
			// 检测结果判断
			if (isset($error))
			{
				// debug 抛出
				throw new Exception($error);
				// 返回
				return $error;
			}
			return true;
		};

		/**
		 * requestRoleSet_run 函数功能运行
		 * @param  string $name   请求标识
		 * @param  int $num  限制请求次数 默认30次
		 * @param  int $time 设置从新开启时常 默认1小时
		 * @param  string $tag  设置缓存标签 默认空
		 * @return  
		 */
		$requestRoleSet_run = function($name,$num,$time,$tag)
		{
			$datacache = Cache::tag($tag)->get($name);
			//如果缓存中的标识不存在，说明未进行缓存，则进行存储缓存记录初始化用户缓存
			if ($datacache == false)
			{
				$datacache['name'] = $name;
				$datacache['num'] = 1;
				$res = Cache::tag($tag)->set($name,$datacache, $time);
				if (false === $res)
				{
					// debug 抛出
					throw new Exception($error);
					// 终止程序返回
					exit(json_encode(['code'=>1,'msg'=>$error,'data'=>false]));
				}
			}
			else
			{
				//如果数缓存中的标识存在，则进行请求+1操作等价缓存请求数
				if ($datacache['name'] == $name)
				{
					$datacache['name'] = $name;
					$datacache['num'] += 1;
					Cache::tag($tag)->set($name,$datacache, $time);
					//如果缓存中加1动作 达到操作次数上限// 返回错误信息
					if ($datacache['num'] > $num) return 'Request_Num_Exceeds_Limit';
				}
				// 如果不相等则说明是新用户，进行初始化操作
				else
				{
					$datacache['name'] = $name;
					$datacache['num'] = 1;
					Cache::tag($tag)->set($name,$datacache, $time);
				}
			}
			return true;
		};
		// 运行检测
		$res = $requestRoleSet_check($name,$num,$time,$tag);
		if (true !== $res) return $res;
		// 运行功能
		$res = $requestRoleSet_run($name,$num,$time,$tag);
		if (true !== $res) return $res;
		else return true;
	}

	/**
	* requestRoleRm 清除ip请求的缓存
	* @param  string $name 请求的缓存名字
	* @param  string $tag  请求的标签名字
	*/
	function requestRoleRm($name,$tag='')
	{
		Cache::tag($tag)->rm($name);
	}

	/**
	 * getCacheData 缓存获取器 作用全站初始化使用
	 * @param  string $tag       缓存标签
	 * @param  string $cachename 缓存名称
	 * @param  string $key       需要存储的键
	 * @param  string $value     需要存储的值
	 * @param  array $config     配置数据部分
	 *                           【switch】缓存是否执行开关 bool
	 *                           【tag】缓存标签
	 *                           【name】表明以及缓存名模型的命名规则下划线admin_user即可找到该模型
	 *                           【key】键设置
	 *                           【value】
	 *                           【relative】相对缓存 既键名与值颠倒 bool
	 *                           【namespace】模型命名空间所在
	 *                           【expire】缓存过期时间
	 *                           【switch_cacke】是否开启缓存检测 bool
	 *                           【cache_refresh】强制刷新缓存 bool
	 */
	public static function getCacheData($tag,$cachename,$key='id',$value='name',$config=[])
	{
		// 读取配置信息 数组下某个详细配置内容规则
		if (empty($config)) $config = Config::get('cache.data_cache.'.$cachename.'');

		/**
		 * getCacheData_check         必传数据检测
		 * @param  string $tag        缓存标签检测
		 * @param  string $cachename  缓存名称检测
		 * @return error              终止程序返回错误
		 */
		$getCacheData_check = function ($tag,$cachename)
		{
			// 检测传参数据是否为空
			if (empty($tag)) $error = 'Cachetool_getCacheData_Paramone_Notempty';
			if (empty($cachename)) $error = 'Cachetool_getCacheData_Paramtwo_Notempty';
			// 检测传参类型是否合法
			if (!is_string($tag)) $error = 'Cachetool_getCacheData_Paramone_Needstring';
			if (!is_string($cachename)) $error = 'Cachetool_getCacheData_Paramone_Needstring';
			// 检测结果判断
			if (isset($error))
			{
				// debug 抛出
				throw new Exception($error);
				// 终止程序返回
				exit(json_encode(['code'=>1,'msg'=>$error,'data'=>false]));
			}
		};

		/**
		* getCacheData_issetCheck   检测缓存是否存在工具
		* @param  string $tag       缓存标签读取
		* @param  string $cachename 缓存实际名称
		* @param  array $config     传入配置信息
		* @return array             如果缓存检测开启 检测空返回报错信息
		* @return array             通常返回缓存信息
		*/
		$getCacheData_issetCheck = function ($tag,$cachename,$config)
		{
			//读取缓存
			$cache = Cache::tag($tag)->get($cachename);
			// 检测缓存配置，是否开启了该缓存报错处理
			if (true === $config['switch_cacke'])
			{	
				// 如果缓存为假的或者空的  终止程序执行返回报错信息
				if (false === $cache || empty($cache)) $error = 'Cachetool_getCacheData_Detectchche_Notdata';
			}
			// 检测结果判断
			if (isset($error))
			{
				// debug 抛出
				throw new Exception($error);
				// 终止程序返回
				exit(json_encode(['code'=>1,'msg'=>$error,'data'=>false]));
			}
			return $cache;
		};

		/**
		* modelload 模型读取器
		* @param  string $cachename 传入的必须是一个模型的名称，如果模型有多个大写则中间加载带下划线
		* @return error            超过最大成熟时，终止程序抛出异常
		* @return string            返回模型名称
		*/
		$getCacheData_modelLoad = function ($cachename)
		{
			// 对缓存模型进行处理 最大4层 拆分缓存名称 此句为模型命名规则【重点】
			$array = explode('_', $cachename);
			switch (count($array))
			{
				case '1': $Model = ucwords($array[0]); break;
				case '2': $Model = ucwords($array[0]).ucwords($array[1]); break;
				case '3': $Model = ucwords($array[0]).ucwords($array[1]).ucwords($array[2]); break;
				case '4': $Model = ucwords($array[0]).ucwords($array[1]).ucwords($array[2]).ucwords($array[3]); break;
				default: $error = 'Cachetool_getCacheData_Cachenam_Beyondmaxlayer';break;
			}
			// 检测结果判断
			if (isset($error))
			{
				// debug 抛出
				throw new Exception($error);
				// 终止程序返回
				exit(json_encode(['code'=>1,'msg'=>$error,'data'=>false]));
			}
			return $Model;
		};

		/**
		 * getCacheData_dataCheck 数据检测器检测数据是否正常
		 * @param  string $Model 模型层的数据名称（我觉得应该加入别名避免冲突）
		 * @param  string $key   传入键名字段
		 * @param  string $value 传入值的字段
		 * @return error         如果数据查询为空或者数据不符，抛出异常
		 * @return array         返回数据查询后的数据内容
		 */
		$getCacheData_dataCheck = function ($Model,$namespace,$key,$value)
		{
			//获取信息
			$obj = $namespace. $Model;
			$obj = new $obj();
			$data = $obj->all()->toArray();
			// 如果查询数据为空
			if (empty($data)) $error = 	'Cachetool_getCacheData_SelectData_Empty';
			// 字段检测，查看字段是否存在数据中 如果不存在则抛出异常
			foreach ($data as $k => $v)
			{
				$res = array_key_exists($key, $v);
				if (false == $res) $error = 'Cachetool_getCacheData_Keyfield_Notexists';
				$res = array_key_exists($value, $v);
				if (false == $res) $error = 'Cachetool_getCacheData_Valuefield_Notexists';
			}
			// 检测结果判断
			if (isset($error))
			{
				// debug 抛出
				throw new Exception($error);
				// 终止程序返回
				exit(json_encode(['code'=>1,'msg'=>$error,'data'=>false]));
			}
			return $data;
		};

		/**
		 * getCacheData_cacheAction 缓存设置操作
		 * @param  string $list  模型层的数据名称（我觉得应该加入别名避免冲突）
		 * @param  string $key 传入键名字段
		 * @param  string $value 传入值的字段
		 * @return error         如果数据查询为空抛出异常
		 * @return array         返回拼装后的键值对
		 */
		$getCacheData_cacheAction = function ($list,$tag,$cachename,$key,$value,$config)
		{
			//将二维数组中的id取出
			$array1 = array_column($list,$key);
			//将二维数组中的名称取出
			$array2 = array_column($list,$value);
			//从新合并成一维数组数组
			$data = array_combine($array1,$array2);
			// 执行缓存
			if (isset($config['expire'])) {
				$res = Cache::tag($tag)->set($cachename,$data,$config['expire']);
			}else{
				$res = Cache::tag($tag)->set($cachename,$data);
			}
			
			// 如果缓存写入失败，则进行记录等待排查
			if (true !== $res) $error = 'Cachetool_getCacheData_Input_Failure';
			// 相对缓存是否启用
			if (true === $config['relative'])
			{
				//将二维数组中的id取出
				$array1 = array_column($list,$value);
				//将二维数组中的名称取出
				$array2 = array_column($list,$key);
				//从新合并成一维数组数组
				$data = array_combine($array1,$array2);
				// 执行缓存
				if (isset($config['expire'])) {
					$res = Cache::tag($tag)->set($cachename,$data,$config['expire']);
				}else{
					$res = Cache::tag($tag)->set($cachename,$data);
				}
				// 如果缓存写入失败，则进行记录等待排查
				if (true !== $res) $error = 'Cachetool_getCacheData_Input_Failure';
			}
			// 检测结果判断
			if (isset($error))
			{
				// debug 抛出
				throw new Exception($error);
				// 终止程序返回
				exit(json_encode(['code'=>1,'msg'=>$error,'data'=>false]));
			}
			return $data;
		};

		// 缓存必填参数检测
		$getCacheData_check($tag,$cachename);
		// 模型名称获取
		$Model = $getCacheData_modelLoad($cachename);
		// 缓存数据获取
		$Cache = $getCacheData_issetCheck($tag,$cachename,$config);
		// 如果缓存强制刷新开启
		if (true == $config['cache_refresh'])
		{
			// 获取数据信息
			$Data = $getCacheData_dataCheck($Model,$config['namespace'],$key,$value);
			// 进行缓存操作
			$getCacheData_cacheAction($Data,$tag,$cachename,$key,$value,$config);
		}
		else
		{
			// 如果缓存不存在或者没有数据
			if (false === $Cache || empty($Cache))
			{
				// 获取数据信息
				$Data = $getCacheData_dataCheck($Model,$config['namespace'],$key,$value);
				// 进行缓存操作
				$getCacheData_cacheAction($Data,$tag,$cachename,$key,$value,$config);
			}
		}
	}
}